package interceptcrossing.ui;

import interceptcrossing.Intercept;
import interceptcrossing.Intersection;
import interceptcrossing.Point;

import javax.swing.*;
import java.awt.*;
import java.util.Collection;
import java.util.List;

/**
 * @author: Igor Botyan
 * @date: May 7, 2010
 */
public class InterceptPaintUtils {

	static final Color TEXT_COLOR = Color.BLACK;
	static final Color BGCOLOR = Color.WHITE;
	static final Color INTERCEPT_COLOR = Color.RED;
	static final Color INTERSECTION_COLOR = new Color(128, 128, 255);
	static final Color AXIS_COLOR = new Color(220, 220, 220);
	static final Color SWEPT_LINE_COLOR = new Color(32, 128, 32);
	static final Color NEW_INTERCEPT_COLOR = new Color(32, 32, 128);
	static final Color SELECTION_COLOR = new Color(32, 128, 32);
	static final int MARGIN = 30;

	private double maxX;
	private double maxY;

	private JComponent component;

	public InterceptPaintUtils(JComponent component) {
		if (component == null) {
			throw new NullPointerException("Component cannot be null");
		}

		this.component = component;

		Point point = this.fromComponentCoords(component.getWidth(), component.getHeight());
		this.maxX = point.x();
		this.maxY = point.y();
	}

	public void recalculateAbscissaBound(Collection<Intercept> intercepts) {
		double x = Double.MIN_VALUE;

		for (Intercept intercept : intercepts) {
			double x1 = Math.abs(intercept.begin().x());
			double x2 = Math.abs(intercept.end().x());

			if (x1 > x) {
				x = x1;
			}

			if (x2 > x) {
				x = x2;
			}
		}

		this.maxX = x;
	}

	public void recalculateOrdinateBound(Collection<Intercept> intercepts) {
		double y = Double.MIN_VALUE;

		for (Intercept intercept : intercepts) {
			double y1 = Math.abs(intercept.begin().y());
			double y2 = Math.abs(intercept.end().y());

			if (y1 > y) {
				y = y1;
			}

			if (y2 > y) {
				y = y2;
			}
		}

		this.maxY = y;
	}

	public void recalculateBounds(Collection<Intercept> intercepts) {
		this.recalculateAbscissaBound(intercepts);
		this.recalculateOrdinateBound(intercepts);
	}

	public void paintBackground(Graphics2D g2d) {
		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
			RenderingHints.VALUE_ANTIALIAS_ON);

		Color color = g2d.getColor();
		g2d.setColor(InterceptPaintUtils.BGCOLOR);
		g2d.fillRect(0, 0, this.component.getWidth(), this.component.getHeight());
		g2d.setColor(color);
	}

	public void paintSweptLine(Graphics2D g, double x) {
		Color color = g.getColor();

		g.setColor(InterceptPaintUtils.SWEPT_LINE_COLOR);

		int height = this.component.getHeight();
		java.awt.Point p = this.toComponentCoords((int) x, 0);

		g.drawLine(p.x, 0, p.x, height);

		g.setColor(color);
	}

	public void paintAbscissaAxis(Graphics2D g) {
		Color color = g.getColor();
		Stroke stroke = g.getStroke();

		g.setColor(InterceptPaintUtils.AXIS_COLOR);
		float[] dash = {5.0f};
		g.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_SQUARE,
			BasicStroke.JOIN_MITER, 1.0f, dash, 0.0f));

		int width = this.component.getWidth();
		int height = this.component.getHeight();

		g.drawLine(0, height / 2, width, height / 2);

		g.setColor(color);
		g.setStroke(stroke);
	}

	public void paintOrdinateAxis(Graphics2D g) {
		Color color = g.getColor();
		Stroke stroke = g.getStroke();

		g.setColor(InterceptPaintUtils.AXIS_COLOR);
		float[] dash = {5.0f};
		g.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_SQUARE,
			BasicStroke.JOIN_MITER, 1.0f, dash, 0.0f));

		int width = this.component.getWidth();
		int height = this.component.getHeight();

		g.drawLine(width / 2, 0, width / 2, height);

		g.setColor(color);
		g.setStroke(stroke);
	}

	public void paintAxises(Graphics2D g) {
		this.paintAbscissaAxis(g);
		this.paintOrdinateAxis(g);
	}

	public void paintIntercepts(Graphics2D g, List<Intercept> intercepts) {
		for (Intercept intercept : intercepts) {
			this.paintIntercept(g, intercept, intercepts.indexOf(intercept) + 1);
		}
	}

	public void paintIntercepts(Graphics2D g, List<Intercept> intercepts, Intercept currentIntercept) {
		for (Intercept intercept : intercepts) {
			if (currentIntercept != intercept) {
				this.paintIntercept(g, intercept, intercepts.indexOf(intercept) + 1);
			}
		}

		if (currentIntercept != null) {
			this.paintCurrentIntercept(g, currentIntercept, intercepts.indexOf(currentIntercept) + 1);
		}
	}

	public void paintIntersections(Graphics2D g, Collection<Intersection> intersections) {
		Color color = g.getColor();

		g.setColor(InterceptPaintUtils.INTERSECTION_COLOR);

		for (Intersection intersection : intersections) {
			java.awt.Point point = this.toComponentCoords(intersection.point());

			int size = 2;

			g.drawRect(point.x - size, point.y - size, 2 * size, 2 * size);
			g.fillRect(point.x - size, point.y - size, 2 * size, 2 * size);
		}

		g.setColor(color);
	}

	public void paintAddingIntercept(Graphics2D g, int x1, int y1, int x2, int y2) {
		Color color = g.getColor();
		Stroke stroke = g.getStroke();

		g.setColor(InterceptPaintUtils.NEW_INTERCEPT_COLOR);
		float[] dash = {2.0f};
		g.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_SQUARE,
			BasicStroke.JOIN_MITER, 1.0f, dash, 0.0f));

		int radius = 1;

		g.drawLine(x1, y1, x2, y2);
		g.drawOval(x1 - radius, y1 - radius, 2 * radius, 2 * radius);
		g.fillOval(x1 - radius, y1 - radius, 2 * radius, 2 * radius);

		g.setColor(color);
		g.setStroke(stroke);
	}

	private void paintIntercept(Graphics2D g, Intercept intercept, int index) {
		Color color = g.getColor();

		java.awt.Point begin = this.toComponentCoords(intercept.begin());
		java.awt.Point end = this.toComponentCoords(intercept.end());

		int x1 = (int) begin.getX();
		int y1 = (int) begin.getY();
		int x2 = (int) end.getX();
		int y2 = (int) end.getY();

		g.setColor(InterceptPaintUtils.INTERCEPT_COLOR);
		g.drawLine(x1, y1, x2, y2);

		this.paintInterceptBoundPoint(g, begin);
		this.paintInterceptBoundPoint(g, end);

		g.setColor(InterceptPaintUtils.TEXT_COLOR);

		char[] data = Integer.toString(index).toCharArray();
		g.drawChars(data, 0, data.length, x2 + 5, y2 + 5);

		g.setColor(color);
	}

	private void paintCurrentIntercept(Graphics2D g, Intercept currentIntercept, int index) {
		Color color = g.getColor();

		java.awt.Point begin = this.toComponentCoords(currentIntercept.begin());
		java.awt.Point end = this.toComponentCoords(currentIntercept.end());

		int x1 = (int) begin.getX();
		int y1 = (int) begin.getY();
		int x2 = (int) end.getX();
		int y2 = (int) end.getY();

		g.setColor(InterceptPaintUtils.SELECTION_COLOR);
		g.drawLine(x1, y1, x2, y2);

		this.paintCurrentInterceptBoundPoint(g, begin);
		this.paintCurrentInterceptBoundPoint(g, end);

		g.setColor(InterceptPaintUtils.TEXT_COLOR);

		char[] data = Integer.toString(index).toCharArray();
		g.drawChars(data, 0, data.length, x2 + 5, y2 + 5);

		g.setColor(color);
	}

	public void paintInterceptBoundPoint(Graphics2D g2d, java.awt.Point point) {
		this.paintInterceptBoundPoint(g2d, point, INTERCEPT_COLOR);
	}

	public void paintCurrentInterceptBoundPoint(Graphics2D g2d, java.awt.Point point) {
		this.paintInterceptBoundPoint(g2d, point, SELECTION_COLOR);
	}

	private void paintInterceptBoundPoint(Graphics2D g2d, java.awt.Point point, Color color) {
		Color prevColor = g2d.getColor();
		g2d.setColor(color);

		int radius = 1;

		g2d.drawOval((int) point.getX() - radius, (int) point.getY() - radius,
			2 * radius, 2 * radius);
		g2d.fillOval((int) point.getX() - radius, (int) point.getY() - radius,
			2 * radius, 2 * radius);

		g2d.setColor(prevColor);
	}

	public void paintIntersectionPoint(Graphics2D g2d, java.awt.Point point) {
		Color prevColor = g2d.getColor();
		g2d.setColor(INTERSECTION_COLOR);

		int radius = 2;

		g2d.drawOval((int) point.getX() - radius, (int) point.getY() - radius,
			2 * radius, 2 * radius);
		g2d.fillOval((int) point.getX() - radius, (int) point.getY() - radius,
			2 * radius, 2 * radius);

		g2d.setColor(prevColor);
	}

	public java.awt.Point toComponentCoords(interceptcrossing.Point point) {
		return this.toComponentCoords((int) point.x(), (int) point.y());
	}

	public java.awt.Point toComponentCoords(int x, int y) {
		int width = this.component.getWidth();
		int height = this.component.getHeight();

		int x0 = width / 2;
		int y0 = height / 2;

		int screenX = (int) (x * (width - MARGIN) / (2 * this.maxX));
		int screenY = (int) (y * (height - MARGIN) / (2 * this.maxY));

		return new java.awt.Point(screenX + x0, y0 - screenY);
	}

	public interceptcrossing.Point fromComponentCoords(int screenX, int screenY) {
		int width = this.component.getWidth();
		int height = this.component.getHeight();

		int x0 = width / 2;
		int y0 = height / 2;

		int x = (int) (2 * this.maxX * (screenX - x0)) / (width - MARGIN);
		int y = (int) (2 * this.maxY * (y0 - screenY)) / (height - MARGIN);

		return new interceptcrossing.Point(x, y);
	}
}
